package com.celeritymq.message;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.StreamMessage;

import com.celeritymq.utils.ObjectUtil;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;

public class CelerityStreamMessage extends CelerityMessage implements StreamMessage {
	private byte[] byteMessage;
	private int writeIndex;
	private int readIndex;
	private static final int DESCRIPTION_LENGTH = 4;

	// private ByteInputStream bis;
	// private ByteOutputStream bos;
	protected CelerityStreamMessage(String messageId, String correlationID, Destination destination, int priority,
			int deliveryMode, long expirationTime) {
		super(messageId, correlationID, destination, priority, deliveryMode, expirationTime);
		// bis=new ByteInputStream();
		// bos=new ByteOutputStream();
		byteMessage = new byte[1024];
		writeIndex = 0;
		readIndex = 0;
	}

	@Override
	public boolean readBoolean() throws JMSException {
		Boolean value = (Boolean) readObject();
		return value;
	}

	@Override
	public byte readByte() throws JMSException {
		Byte value = (Byte) readObject();
		return value;
	}

	@Override
	public int readBytes(byte[] value) throws JMSException {
		int length = value.length;
		int i = 0;
		for (; i < length && readIndex < byteMessage.length; i++) {
			value[i] = byteMessage[readIndex++];
		}
		if (i < readIndex) {
			return i;
		} else {
			return readIndex;
		}
	}

	@Override
	public char readChar() throws JMSException {
		Character c = (Character) readObject();
		return c;
	}

	@Override
	public double readDouble() throws JMSException {
		Double d = (Double) readObject();
		return d;
	}

	@Override
	public float readFloat() throws JMSException {
		Float f = (Float) readObject();
		return f;
	}

	@Override
	public int readInt() throws JMSException {
		Integer i = (Integer) readObject();
		return i;
	}

	@Override
	public long readLong() throws JMSException {
		Long l = (Long) readObject();
		return l;
	}

	@Override
	public Object readObject() throws JMSException {
		byte[] objectLengthArray = new byte[DESCRIPTION_LENGTH];
		for (int i = 0; i < DESCRIPTION_LENGTH; i++) {
			objectLengthArray[i] = byteMessage[readIndex++];
		}
		int arrayLength = ObjectUtil.recoverInt(objectLengthArray);
		byte[] objectArray = new byte[arrayLength];
		for (int i = 0; i < arrayLength; i++) {
			objectArray[i] = byteMessage[readIndex++];
		}
		Object obj = null;
		ObjectInputStream ois = null;
		try {
			ois = new ObjectInputStream(new ByteArrayInputStream(objectArray));
			obj = ois.readObject();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (ois != null) {
				try {
					ois.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return obj;
	}

	@Override
	public short readShort() throws JMSException {
		Short s = (Short) readObject();
		return s;
	}

	@Override
	public String readString() throws JMSException {
		String s = (String) readObject();
		return s;
	}

	@Override
	public void reset() throws JMSException {
		writeIndex = 0;
	}

	@Override
	public void writeBoolean(boolean v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeByte(byte v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeBytes(byte[] v) throws JMSException {
		writeBytes(v, 0, v.length);
	}

	@Override
	public void writeBytes(byte[] value, int offset, int length) throws JMSException {
		int writeLength = length - offset;
		// write bytes array total length in to byteMessage array
		writeBytesLength(writeLength);
		// check byteMessage's length, if length can not meet current writen
		// length, expand byteMessage array
		checkCapacity(DESCRIPTION_LENGTH + writeLength);
		// write value body
		for (int i = offset + 1; i < value.length; i++) {
			byteMessage[writeIndex++] = value[i];
		}
	}

	private void writeBytesLength(int length) {
		byte[] lengthArray = ObjectUtil.parse(length);
		for (int i = 0; i < lengthArray.length; i++) {
			byteMessage[writeIndex++] = lengthArray[i];
		}
	}

	@Override
	public void writeChar(char v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeDouble(double v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeFloat(float v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeInt(int v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeLong(long v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeObject(Object arg) throws JMSException {
		if (arg instanceof Serializable) {
			byte[] value = ObjectUtil.parse((Serializable) arg);
			writeBytes(value);
		} else {
			throw new JMSException(arg + " is not a serializable object!");
		}
	}

	@Override
	public void writeShort(short v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void writeString(String v) throws JMSException {
		writeObject(v);
	}

	@Override
	public void clearBody() throws JMSException {
		byteMessage = new byte[1024];
	}

	private void checkCapacity(int length) {
		if (length > 1024 * 100) {

		}
		if (writeIndex + length > byteMessage.length) {
			int newLength = writeIndex + length + 512;
			byteMessage = new byte[newLength];
			checkCapacity(newLength);
		}
	}

	// static int position2=0;
	// static byte[] byteMessage2;
	// private static byte[] checkCapacityTest(byte[] src, byte[] addArray){
	// if(position2+addArray.length>src.length){
	// byteMessage2=new byte[position2+addArray.length+512];
	// return checkCapacityTest(byteMessage2,addArray);
	// }
	//
	// return byteMessage2;
	// }
	// public static void main(String[] args){
	// byteMessage2=new byte[5];
	//
	// byte[] b2=new byte[50];
	//
	// byteMessage2=checkCapacityTest(byteMessage2, b2);
	// System.out.println(byteMessage2.length);
	// }

}
